/**
 * @file utils.hpp
 * @brief 实用工具类模块的实现
 *   - mysql_util   封装mysqlclient的工具类
 *   - json_util    封装jsoncpp提供序列化、反序列化操作的工具类
 *   - string_util  提供字符串分割功能的工具类
 *   - file_util    提供文件读取功能的工具类
 */
#pragma once
#include "logger.hpp"
#include <mysql/mysql.h>
#include <jsoncpp/json/json.h>
#include <memory>
#include <sstream>
#include <vector>

class mysql_util {
public:
    /**
     * @brief 创建一个 MySQL 连接并返回 MySQL 句柄。
     *        该过程包括初始化、连接服务器、设置客户端字符集等。
     * @param host      MySQL 服务器地址
     * @param user      数据库用户名
     * @param passwd    数据库密码
     * @param dbname    要连接的数据库名称
     * @param port      MySQL 服务器端口（默认为 3306）
     * @return MYSQL* 
     *   - 成功: 返回有效的 MySQL 句柄
     *   - 失败: 返回 nullptr，并在日志中记录错误信息
     */
    static MYSQL *mysql_create(const std::string &host,
                            const std::string &user,
                            const std::string &passwd,
                            const std::string &dbname,
                            uint32_t port) {
        // 初始化 MySQL 句柄
        MYSQL *mysql = mysql_init(nullptr); 
        if (mysql == nullptr) {
            LOG(ERROR, "初始化MySQL句柄失败\n");
            return nullptr;
        }
        // 连接到 MySQL 服务器
        if (mysql_real_connect(mysql, host.c_str(), user.c_str(), 
            passwd.c_str(), dbname.c_str(), port, nullptr, 0) == nullptr) {
            LOG(ERROR, "连接MySQL服务器失败, 错误信息: %s\n", mysql_error(mysql));
            mysql_close(mysql);
            return nullptr;
        }
        // 设置客户端字符集为 UTF-8
        if (mysql_set_character_set(mysql, "utf8") != 0) {
            LOG(ERROR, "设置MySQL字符集失败, 错误信息: %s\n", mysql_error(mysql));
            mysql_close(mysql);
            return nullptr;
        }
        // 返回有效的 MySQL 句柄
        return mysql;
    }

    /**
     * @brief 执行传入的 SQL 语句，并判断是否执行成功。
     * @param mysql     MySQL句柄
     * @param sql       要执行的 SQL 语句
     * @return bool
     *   - 成功: 返回 true
     *   - 失败: 返回 false，并在日志中记录错误信息
     */
    static bool mysql_execute(MYSQL *mysql, const std::string &sql) {
        // 执行 SQL 查询
        if (mysql_query(mysql, sql.c_str()) != 0) {
            LOG(ERROR, "SQL语句: %s\n", sql.c_str());
            LOG(ERROR, "错误信息: %s\n", mysql_error(mysql));
            mysql_close(mysql);
            return false;
        }
        return true;
    }

    /**
     * @brief 关闭并释放传入的 MySQL 连接句柄。
     * @param mysql MySQL句柄
     */
    static void mysql_destroy(MYSQL *mysql) {
        if (mysql != nullptr) {
            mysql_close(mysql);
        }
    }
};

class json_util {
public:
    /**
     * @brief 将 Json::Value 对象序列化为 JSON 字符串。
     * @param json_value    要序列化的 Json::Value 对象
     * @param json_string   输出的 JSON 字符串
     * @return bool
     *   - 成功: 返回 true
     *   - 失败: 返回 false，并在日志中记录错误信息
     */
    static bool serialize(const Json::Value &json_value, std::string &json_string) {
        Json::StreamWriterBuilder writer_factory; // 创建 JSON 写入器工厂
        std::unique_ptr<Json::StreamWriter> json_writer(writer_factory.newStreamWriter()); // 创建写入器对象
        std::stringstream json_stream; // 用于存储序列化结果
        // 序列化 JSON 对象为字符串
        if (json_writer->write(json_value, &json_stream) != 0) {
            LOG(ERROR, "Json对象序列化为JSON字符串失败\n");
            return false; // 返回失败
        }
        json_string = json_stream.str(); // 获取序列化结果
        return true; // 返回成功
    }

    /**
     * @brief 将 JSON 字符串反序列化为 Json::Value 对象。
     * @param json_string   要反序列化的 JSON 字符串
     * @param json_value    输出的 Json::Value 对象
     * @return bool
     *   - 成功: 返回 true
     *   - 失败: 返回 false，并输出错误信息
     */
    static bool deserialize(const std::string &json_string, Json::Value &json_value) {
        Json::CharReaderBuilder reader_factory; // 创建 JSON 读取器工厂
        std::unique_ptr<Json::CharReader> json_reader(reader_factory.newCharReader()); // 创建读取器对象
        std::string error_string; // 存储错误信息
        // 反序列化 JSON 字符串为对象
        if (json_reader->parse(json_string.c_str(), 
                            json_string.c_str() + json_string.size(), 
                            &json_value, &error_string) == false) {
            std::cout << "JSON字符串反序列化为Json对象失败\n" << error_string; // 输出错误信息
            return false; // 返回失败
        }
        return true; // 返回成功
    }
};

class string_util {
public:
    /**
     * @brief 将源字符串按指定分隔符分割为多个子串。
     * @param src 要分割的源字符串
     * @param sep 分隔符
     * @param res 输出的子串向量
     * @return int
     *   - 返回子串数量
     *   - 如果分隔符为空，返回 -1 表示错误
     *   - 如果源字符串为空，返回 0 表示没有子串
     */
    static int split(const std::string &src, const std::string &sep, std::vector<std::string> &res) {
        if (sep.empty()) {
            LOG(ERROR, "分隔符不能为空\n");
            return -1;
        }
        if (src.empty()) {
            return 0;
        }
        size_t pos = 0, index = 0; // pos 用于查找位置，index 用于记录当前索引
        while (index < src.size()) {
            pos = src.find(sep, index); // 从index位置查找sep
            if (pos == std::string::npos) {
                res.push_back(src.substr(index)); // 获取最后一个子串
                break;
            }
            if (pos > index) {
                res.push_back(src.substr(index, pos - index)); // 获取非空部分
            }
            index = pos + sep.size(); // 更新索引
        }
        return res.size(); // 返回子串数量
    }
};

class file_util {
public:
    /**
     * @brief 从指定文件读取内容并存入字符串。
     * @param filename  要读取的文件名
     * @param body      输出的文件内容
     * @return bool
     *   - 成功: 返回 true;
     *   - 失败: 返回 false，并在日志中记录错误信息;
     */
    static bool read(const std::string &filename, std::string &body) {
        std::ifstream ifs(filename, std::ios::binary); // 以二进制模式打开文件
        if (!ifs.is_open()) {
            LOG(ERROR, "%s文件打开失败\n", filename.c_str());
            return false;
        }
        ifs.seekg(0, std::ios_base::end);   // 移动到文件末尾
        size_t file_size = ifs.tellg();     // 获取文件大小
        ifs.seekg(0, std::ios_base::beg);   // 重置流指针到文件开头
        body.resize(file_size);             // 调整字符串大小以适应文件内容
        ifs.read(&body[0], file_size);      // 从文件中读取数据到字符串
        if (!ifs.good()) {
            LOG(ERROR, "%s文件读取失败\n", filename.c_str());
            return false; // 返回失败
        }
        ifs.close(); // 关闭文件
        return true; // 返回成功
    }
};
